module ds18b20_driver (
    input               clk,
    input               rst_n,
    input     [4:0]     sw,
    // input               dq_in,
    // output reg          dq_out,
    // output reg          dq_en,
    inout               dq,
    output reg [15:0]   temp_data
);

//状态机数据
reg     [3:0]           state_c;    //现态
reg     [3:0]           state_n;    //次态

reg     [2:0]           s_state_c;
reg     [2:0]           s_state_n;

//主状态定义
localparam  IDLE = 4'b0000,
            SEND = 4'b0001,
            RECV = 4'b0010,
            SKIP = 4'b0011,
            CT   = 4'b0100,
            WS   = 4'b1000,         //配置精度命令状态
            WD   = 4'b1001,         //配置精度数据状态
            WAIT = 4'b0101,
            RC   = 4'b0110,
            RD   = 4'b0111;

wire        IDLE_2_SEND,
            SEND_2_RECV,
            RECV_2_SKIP,
            SKIP_2_CT,
            CT_2_WAIT,
            WAIT_2_SEND,

            SKIP_2_RC,
            RC_2_RD,
            RD_2_SEND,

            SKIP_2_WS,
            WS_2_WD,
            WD_2_SEND;

//从状态机定义
// localparam  S_IDLE = 3'b000,
            // S_SLOW = 3'b001,
            // S_READ = 3'b010,
            // S_SEND = 3'b011,
            // S_RELS = 3'b100,
            // S_DOWN = 3'b101;

// wire        S_IDLE_2_SLOW,
            // S_SLOW_2_READ,
            // S_SLOW_2_SEND,
            // S_SEND_2_RELS,
            // S_READ_2_RELS,
            // S_RELS_2_SLOW,
            // S_RELS_2_DOWN,
            // S_DOWN_2_IDLE;

//三态门数据
reg                     dq_en; 
wire                    dq_in;
reg                     dq_out;  

//计2us
reg        [6:0]         cnt_2us;
wire                     add_cnt_2us;
wire                     end_cnt_2us;
parameter                TIME_2us = 100;

//读数据的10us
// reg        [9:0]         cnt_10us;
// wire                     add_cnt_10us;
// wire                     end_cnt_10us;
parameter                TIME_10us = 500;

//写计数100us
reg        [12:0]        cnt_100us;
wire                     add_cnt_100us;
wire                     end_cnt_100us;
parameter                TIME_100us = 5_000;
parameter                TIME_1us  = 50;

//复位脉冲和存在脉冲的480us计数
reg        [14:0]       cnt_480us;
wire                    add_cnt_480us;
wire                    end_cnt_480us; 
parameter               TIME_480us = 24_000;

//等待750ms计数
reg        [25:0]        cnt_750ms;
wire                     add_cnt_750ms;
wire                     end_cnt_750ms;
parameter                TIME_750ms = 37_500_000,
                         TIME_375ms = 18_750_000,
                         TIME_187_5ms = 9_375_000,
                         TIME_93_75ms = 4_687_500;

reg        [25:0]        time_ms;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        time_ms <= 37_500_000;
    end
    else begin
    case(sw[4:3]) 
        2'b00: time_ms <= TIME_93_75ms;
        2'b01: time_ms <= TIME_187_5ms;
        2'b10: time_ms <= TIME_375ms;
        2'b11: time_ms <= TIME_750ms;        
        default: time_ms = 0;
    endcase
    end
end

//ROM指令与功能命令的8bit计数
reg        [2:0]        cnt_8bit;
wire                    add_cnt_8bit;
wire                    end_cnt_8bit;
parameter               MAX_8bit = 8;

//配置寄存器的三个字节的8bit计数
reg        [1:0]        cnt_3num;
wire                    add_cnt_3num;
wire                    end_cnt_3num;
parameter               MAX_3num = 3;

//读取到的16bit计数
reg        [3:0]         cnt_16bit;
wire                     add_cnt_16bit;
wire                     end_cnt_16bit;
parameter                MAX_16bit = 16;

reg                     idle_flag;                   //存在脉冲标志
reg     [1:0]           skip_flag;                   //跳转标志

//三态门
assign dq_in    = dq;
assign dq       = dq_en ? dq_out : 1'bz;

//2us计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_2us <= 7'd0;
    end 
    else if(add_cnt_2us)begin
        if (end_cnt_2us) begin
            cnt_2us <= 7'd0;
        end 
        else begin
            cnt_2us <= cnt_2us + 7'd1;
            end
    end
    else begin
        cnt_2us <= cnt_2us;
    end
end
assign add_cnt_2us = (state_c == IDLE);  //(s_state_c == S_IDLE)||(s_state_c == S_DOWN);
assign end_cnt_2us = add_cnt_2us && (cnt_2us == TIME_2us-1);

//10us计数器
// always @(posedge clk or negedge rst_n) begin
    // if (!rst_n) begin
        // cnt_10us <= 10'b0;
    // end 
    // else if(add_cnt_10us)begin
        // if (end_cnt_10us) begin
            // cnt_10us <= 10'b0;
        // end 
        // else begin
            // cnt_10us <= cnt_10us + 10'b1;
        // end
    // end
    // else begin
        // cnt_10us <= cnt_10us;
    // end
// end
// assign add_cnt_10us = (s_state_c == S_READ);
// assign end_cnt_10us = add_cnt_10us && (cnt_10us == (TIME_10us-1));

//100us计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_100us <= 13'd0;
    end 
    else if(add_cnt_100us)begin
        if (end_cnt_100us) begin
            cnt_100us <= 13'd0;
        end 
        else begin
            cnt_100us <= cnt_100us + 13'd1;
        end
    end
    else begin
        cnt_100us <= cnt_100us;
    end
end
assign add_cnt_100us = (state_c == SKIP)||(state_c == CT)||(state_c == WS)||(state_c == WD)||(state_c == RC)||(state_c == RD);
assign end_cnt_100us = add_cnt_100us && (cnt_100us == (TIME_100us-1));

//480us计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_480us <= 15'd0;
    end 
    else if(add_cnt_480us)begin
        if (end_cnt_480us) begin
            cnt_480us <= 15'd0;
        end 
        else begin
            cnt_480us <= cnt_480us + 15'd1;
        end
    end
    else begin
        cnt_480us <= cnt_480us;
    end
end

assign add_cnt_480us = (state_c == SEND)||(state_c == RECV);
assign end_cnt_480us = add_cnt_480us && (cnt_480us == TIME_480us-1);

//等待750ms计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_750ms <= 26'd0;
    end 
    else if(add_cnt_750ms)begin
        if (end_cnt_750ms) begin
            cnt_750ms <= 26'd0;
        end 
        else begin
            cnt_750ms <= cnt_750ms + 26'd1;
        end
    end
    else begin
        cnt_750ms <= cnt_750ms;
    end
end
assign add_cnt_750ms = (state_c == WAIT);
assign end_cnt_750ms = add_cnt_750ms &&(cnt_750ms == (time_ms-1));

//ROM指令与功能命令的8bit计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_8bit <= 3'd0;
    end 
    else if(add_cnt_8bit)begin
        if (end_cnt_8bit) begin
            cnt_8bit <= 3'd0;
        end 
        else begin
            cnt_8bit <= cnt_8bit + 3'd1;
        end
    end
    else begin
        cnt_8bit <= cnt_8bit;
    end
end
assign add_cnt_8bit = (end_cnt_100us) && ((state_c == CT)||(state_c == SKIP)||(state_c == RC)||(state_c == WS)||(state_c == WD));
assign end_cnt_8bit = add_cnt_8bit && (cnt_8bit == (MAX_8bit-1));

//配置寄存器的三个字节的8bit计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_3num <= 2'd0;
    end 
    else if(add_cnt_3num)begin
        if (end_cnt_3num) begin
            cnt_3num <= 2'd0;
        end 
        else begin
            cnt_3num <= cnt_3num + 2'd1;
        end
    end
    else begin
        cnt_3num <= cnt_3num;
    end
end

assign add_cnt_3num = (end_cnt_8bit) && (state_c == WD);
assign end_cnt_3num = add_cnt_3num && (cnt_3num == (MAX_3num-1));

//16bit 计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_16bit <= 4'd0;
    end 
    else if(add_cnt_16bit)begin
        if (end_cnt_16bit) begin
            cnt_16bit <= 4'd0;
        end 
        else begin
            cnt_16bit <= cnt_16bit + 4'd1;
        end
    end
    else begin
        cnt_16bit <= cnt_16bit;
    end
end
assign add_cnt_16bit = (end_cnt_100us) &&(state_c == RD) ;
assign end_cnt_16bit = add_cnt_16bit && (cnt_16bit == (MAX_16bit-1));

//存在脉冲描述
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        idle_flag <=0;
    end 
    else if((cnt_480us == 5000) && (dq_in == 0))begin   //100us时检测存在脉冲
        idle_flag <= 1;
    end
    else if(state_c == SKIP)begin
        idle_flag <= 0;
    end
    else begin
        idle_flag <= idle_flag;
    end
end

//skip跳转标志
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        skip_flag <= 2'd0;                 //0为跳转温度转换，1为跳转配置寄存器，2为跳转读温度
    end
    else if(WAIT_2_SEND)begin
        skip_flag <= 2'd1;
    end
    else if(WD_2_SEND)begin
        skip_flag <= 2'd2;
    end
    else if(RD_2_SEND)begin
        skip_flag <= 2'd0;
    end
    else begin
        skip_flag <= skip_flag;
    end
end

//主状态机一段
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end

//从状态机一段
// always @(posedge clk or negedge clk ) begin
    // if(!rst_n)begin
        // s_state_n <= S_IDLE;
    // end
    // else begin
        // s_state_n <= s_state_c;
    // end
// end

//主状态机二段
always @(*) begin
    case(state_c)
        IDLE : begin state_n <= IDLE_2_SEND ? SEND : state_c; end
        SEND : begin state_n <= SEND_2_RECV ? RECV : state_c; end
        RECV : begin state_n <= RECV_2_SKIP ? SKIP : state_c; end
        SKIP : begin if(SKIP_2_CT) begin state_n <=  CT   ;end
                     else if (SKIP_2_RC)  begin   state_n <= RC   ;end
                     else if (SKIP_2_WS)  begin   state_n <= WS   ;end
                     else begin state_n <= state_c; end
        end
        CT   : begin state_n <= CT_2_WAIT   ? WAIT : state_c; end
        WAIT : begin state_n <= WAIT_2_SEND ? SEND : state_c; end

        WS   : begin state_n <= WS_2_WD     ? WD   : state_c; end
        WD   : begin state_n <= WD_2_SEND   ? SEND : state_c; end

        RC   : begin state_n <= RC_2_RD     ? RD   : state_c; end
        RD   : begin state_n <= RD_2_SEND   ? SEND : state_c; end
    endcase
end

//描述跳转条件
assign  IDLE_2_SEND = (state_c == IDLE) && end_cnt_2us;
assign  SEND_2_RECV = (state_c == SEND) && end_cnt_480us;
assign  RECV_2_SKIP = (state_c == RECV) && end_cnt_480us && (idle_flag == 1);
assign  SKIP_2_CT   = (state_c == SKIP) && end_cnt_8bit  && (skip_flag == 0);
assign  CT_2_WAIT   = (state_c == CT)   && end_cnt_8bit;
assign  WAIT_2_SEND = (state_c == WAIT) && end_cnt_750ms;

assign  SKIP_2_WS   = (state_c == SKIP) && end_cnt_8bit  && (skip_flag == 1);
assign  WS_2_WD     = (state_c == WS)   && end_cnt_8bit;
assign  WD_2_SEND   = (state_c == WD)   && end_cnt_3num;

assign  SKIP_2_RC   = (state_c == SKIP) && end_cnt_8bit  && (skip_flag == 2);
assign  RC_2_RD     = (state_c == RC)   && end_cnt_8bit;
assign  RD_2_SEND   = (state_c == RD)   && end_cnt_16bit;

//第三段状态机
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        dq_en       <= 0;
        dq_out      <= 0;
        temp_data   <= 0;
    end
    else begin
        case(state_c) 
                IDLE : begin            //释放总线
                    dq_en       <= 0;
                    dq_out      <= 0;
                end
                SEND : begin            //初始化拉低总线
                    dq_en       <= 1;
                    dq_out      <= 0;
                end
                RECV : begin            //释放总线检测存在脉冲
                    dq_en       <= 0;
                    dq_out      <= 0;
                end
                SKIP    :   begin       //发送rom指令 跳过rom cc 1100 1100
                            if(cnt_8bit == 3'd2 || cnt_8bit == 3'd3 || cnt_8bit == 3'd6 || cnt_8bit == 3'd7)begin   //写1
                                if(cnt_100us <= 100)begin  //拉低2us
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else if(cnt_100us > 100 && cnt_100us < 3100)begin //拉高60us
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b1;
                                end
                                else begin                  //释放总线 写0写1之间需要释放总线大于1us
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                            else begin  //写0
                                if(cnt_100us < 3100)begin    //拉低62us
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else begin                  //释放总线
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                end
                CT      :   begin   //发送功能命令 温度转换 44 0100_0100
                            if(cnt_8bit == 3'd2 || cnt_8bit == 3'd6)begin  //写1
                                if(cnt_100us <= 100)begin
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else if(cnt_100us > 100 && cnt_100us < 3100)begin
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b1;
                                end
                                else begin
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                            else begin      //写0
                                if(cnt_100us < 3100)begin
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else begin
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                end
                WS      :   begin   //发送功能命令 配置寄存器 4e 0100_1110
                            if(cnt_8bit == 3'd1 || cnt_8bit == 3'd2|| cnt_8bit == 3'd3|| cnt_8bit == 3'd6)begin  //写1
                                if(cnt_100us <= 100)begin//拉低2us
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else if(cnt_100us > 100 && cnt_100us < 3100)begin//拉高60us
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b1;
                                end
                                else begin
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                            else begin      //写0
                                if(cnt_100us < 3100)begin
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else begin
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                end
                WD      :   begin   //发送配置寄存器的数据 
                            if((cnt_3num==2)&&(cnt_8bit == 3'd7 || ((sw[3]==0)&&cnt_8bit==3'd6)||((sw[4]==0)&&cnt_8bit==3'd5))) begin      //写0
                                if(cnt_100us < 3100)begin
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else begin
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                            else begin  //写1
                                if(cnt_100us <= 100)begin//拉低2us
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else if(cnt_100us > 100 && cnt_100us < 3100)begin//拉高60us
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b1;
                                end
                                else begin
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                end
                WAIT    :   begin   //等待转换完成 750ms
                            dq_en  <= 1'b0;
                            dq_out <= 1'b0;
                end
                RC      :   begin   //发送读取温度指令  BE  1011_1110
                            if(cnt_8bit == 3'd0 || cnt_8bit == 3'd6)begin  //写0
                                if(cnt_100us < 3100)begin
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else begin
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                            else begin      //写1
                                if(cnt_100us <= 100)begin
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b0;
                                end
                                else if(cnt_100us > 100 && cnt_100us < 3100)begin
                                    dq_en  <= 1'b1;
                                    dq_out <= 1'b1;
                                end
                                else begin
                                    dq_en  <= 1'b0;
                                    dq_out <= 1'b0;
                                end
                            end
                end
                RD      :   begin   //读取16bit温度数据
                            if(cnt_100us < 100)begin  //拉低2us
                                dq_en  <= 1'b1;
                                dq_out <= 1'b0;
                            end
                            else begin  //释放总线 由从机控制总线传数据到主机
                                dq_en  <= 1'b0;
                                dq_out <= 1'b0;
                                if(cnt_100us == TIME_10us)begin           //10us的时候进行采样
                                    temp_data[cnt_16bit] <= dq_in;     //串并转换
                                end
                            end
                end
                default : begin
                    dq_en  <= 1'b0;
                    dq_out <= 1'b0;
                    temp_data <= 16'h0000;
                end
        endcase
    end
end
    
endmodule